/*
 * RemoteInvoker.java   0.0.1    Nov 17, 2009
 * 
 * Copyright (c) 2009 mentalsmash.org
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of the Apache License does not indicate that this project is
 * affiliated with the Apache Software Foundation.
 */
package org.mentalsmash.tazio.net.ril.impl;

import org.mentalsmash.tazio.commons.identifiers.OID;
import org.mentalsmash.tazio.net.ril.DispatchingLayerProxy;
import org.mentalsmash.tazio.net.ril.RemoteInvocationConfiguration;
import org.mentalsmash.tazio.net.ril.RemoteInvocationDispatcher;
import org.mentalsmash.tazio.net.ril.RemoteInvocationMessageHandler;
import org.mentalsmash.tazio.net.ril.RemoteInvoker;
import org.mentalsmash.tazio.net.ril.RemoteObject;
import org.mentalsmash.tazio.net.ril.RemoteObjectProxy;
import org.mentalsmash.tazio.net.ril.RemoteObjectsRegistry;
import org.mentalsmash.tazio.net.ril.exceptions.NoSuchRemoteObjectException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;

/**
 * This class is the main (and only) "entry point" to Remote Invocation Layer.<br/>
 * A RemoteInvoker instance allows a client to register new instances for remote
 * invocation of their methods, by means of its {@link RemoteObjectsRegistry},
 * and offers convenient methods to retrieve local "proxy instances" to
 * instances registered on other RemoteInvokers (possibly executing on different
 * hosts) to be used for transparent invocation of their methods as if the
 * instances were actually executing in the local environment.<br/>
 * 
 * The use of these proxies relieves clients of Remote Invocation Layer from
 * having to use anything more than a small set of the methods provided (
 * {@link BaseRemoteInvoker#createInvoker(RemoteInvocationConfiguration)},
 * {@link BaseRemoteInvoker#getRemoteObject(OID, Class)},
 * {@link RemoteObjectsRegistry#registerObject(OID, Object)} and a few more).<br/>
 * 
 * Anyway, when accessing methods on remote instances by means of one of the
 * proxies, clients should be aware that, while this is usually carried out
 * transparently with nothing more to do than if the method had executed
 * locally, since remote invocation is subject to possible failures which don't
 * usually apply to standard local invocations (most notably failing in
 * communicating with some other RemoteInvoker), it is possible for a runtime
 * exception to be thrown if one of such failures occurred and prevented the
 * invocation from completing correctly. See {@link RemoteObjectProxy} for a
 * detailed explanation of how transparent remote invocation is carried out and
 * what are the possible concerns involved in it.<br/>
 * 
 * The following describes the role of each component used by a RemoteInvoker,
 * mainly for developing and deployment of Remote Invocation Layer.<br/>
 * 
 * Discovering of and communication with other RemoteInvokers is carried out by
 * a {@link DispatchingLayerProxy}, which might employ different strategies,
 * depending on the specific deployment of the Remote Invocation Layer. This
 * components uses the services of some other Dispatching Layer to provides its,
 * using them to send and receive messages to and from other hosts executing a
 * RemoteInvoker (communication) and also to determine which these remote hosts
 * are and how to contact them (discovering).<br/>
 * 
 * Coordination with other RemoteInvokers is taken care of by two other
 * components, a {@link RemoteInvocationDispatcher} and a
 * {@link RemoteInvocationMessageHandler}, both of which use the aforementioned
 * {@link DispatchingLayerProxy} to satisfy their communication needs.<br/>
 * 
 * The first provides for requesting operations on other RemoteInvokers, such as
 * the execution of a method invocation on an remote instance or the retrieving
 * of a proxy to one of the remotely available instances.<br/>
 * The other receives incoming requests and coordinates with the local
 * RemoteInvoker to serve them and return the correct response to the requesting
 * RemoteInvoker.<br/>
 * 
 * Both of these components are easily extensible to fit specific deployment
 * scenarios, which might require custom handling of both sending and handling
 * of requests (e.g. when executing on Weak Replica Managers, a RemoteInvoker
 * should not allow incoming "group method invocations" and rely on one of the
 * Replica Managers to execute the ones it requests - see
 * {@link RemoteInvocationDispatcher} and {@link RemoteInvocationMessageHandler}
 * for more informations about this).
 * 
 * 
 * @version 0.0.1 Nov 17, 2009
 * @author Andrea Sorbini <as@mentalsmash.org>
 * 
 */
class BaseRemoteInvoker implements RemoteInvoker {
    private final static Logger log = LoggerFactory
	    .getLogger(BaseRemoteInvoker.class);

    private RemoteObjectsRegistry registry;
    private RemoteInvocationDispatcher dispatcher;
    private RemoteInvocationMessageHandler handler;
    private DispatchingLayerProxy proxy;

    private final OID oid = OID.generateOID();

    /*
     * (non-Javadoc)
     * 
     * @see org.mentalsmash.tazio.net.ril.IRemoteInvoker#getOID()
     */
    public OID getOID() {
	return this.oid;
    }

    @Inject
    public BaseRemoteInvoker(RemoteObjectsRegistry registry,
	    RemoteInvocationDispatcher dispatcher,
	    RemoteInvocationMessageHandler handler, DispatchingLayerProxy proxy) {
	this.registry = registry;
	this.dispatcher = dispatcher;
	this.handler = handler;
	this.proxy = proxy;
    }
    
//    @Inject
//    public BaseRemoteInvoker(){
//	
//    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mentalsmash.tazio.net.ril.IRemoteInvoker#getRegistry()
     */
    public RemoteObjectsRegistry getRegistry() {
	return registry;
    }

//    public void setRegistry(RemoteObjectsRegistry registry) {
//	this.registry = registry;
//    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mentalsmash.tazio.net.ril.IRemoteInvoker#getDispatcher()
     */
//    public RemoteInvocationDispatcher getDispatcher() {
//	return dispatcher;
//    }

//    public void setDispatcher(RemoteInvocationDispatcher dispatcher) {
//	this.dispatcher = dispatcher;
//    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mentalsmash.tazio.net.ril.IRemoteInvoker#getMessageHandler()
     */
    public RemoteInvocationMessageHandler getMessageHandler() {
	return handler;
    }

//    public void setMessageHandler(RemoteInvocationMessageHandler handler) {
//	this.handler = handler;
//    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.net.ril.IRemoteInvoker#getDispatchingLayerProxy()
     */
//    public DispatchingLayerProxy getDispatchingLayerProxy() {
//	return proxy;
//    }

    /**
     * Sets the {@link DispatchingLayerProxy} instance to be used by this
     * RemoteInvoker to send remote requests to other RemoteInvokers.
     * 
     * @param proxy
     */
//    public void setDispatchingLayerProxy(DispatchingLayerProxy proxy) {
//	this.proxy = proxy;
//    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.net.ril.IRemoteInvoker#getRemoteObject(org.mentalsmash
     * .tazio.core.identifiers.OID)
     */
    public RemoteObject getRemoteObject(OID oid)
	    throws NoSuchRemoteObjectException {

	if (this.registry.hasObject(oid)) {
	    return this.registry.getRemoteObject(oid);
	} else {
	    return this.dispatcher.getRemoteObject(oid);
	}
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.net.ril.IRemoteInvoker#getRemoteObject(org.mentalsmash
     * .tazio.core.identifiers.OID, java.lang.Class)
     */
    @SuppressWarnings("unchecked")
    public <T> T getRemoteObject(OID oid, Class<T> objType)
	    throws NoSuchRemoteObjectException {
	RemoteObject obj = this.getRemoteObject(oid);

	T result = null;

	try {
	    result = (T) obj;
	} catch (Exception ex) {
	    log.error("");
	}

	return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
	final int prime = 31;
	int result = 1;
	result = prime * result + ((oid == null) ? 0 : oid.hashCode());
	return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
	if (this == obj)
	    return true;
	if (obj == null)
	    return false;
	if (getClass() != obj.getClass())
	    return false;
	BaseRemoteInvoker other = (BaseRemoteInvoker) obj;
	if (oid == null) {
	    if (other.oid != null)
		return false;
	} else if (!oid.equals(other.oid))
	    return false;
	return true;
    }

    @Override
    public String toString() {
	return "invoker-" + this.oid;
    }

  
}
